/* -*-c++-*- 
 * This source code is proprietary of ADIT
 * Copyright (C) 2013 Advanced Driver Information Technology Joint Venture GmbH
 * All rights reserved
 *
 * Author: Vadiraj Kaamsha <vadiraj.kaamsha@in.bosch.com>
 * Author: Rudolf Dederer <rudolf.dederer@de.bosch.com>
*/

#ifndef GLYPH_INFO_CONTAINER
#define GLYPH_INFO_CONTAINER 1

#include <osgBatchedText/TextConfigBase>
#include <osg/Referenced>
#include <OpenThreads/Mutex>

namespace osgBatchedText {

const unsigned int INVALID_GLYPH_INDEX = 0xFFFFFFFF;

class OSGBATCHEDTEXT_EXPORT BatchEntry
{
public:
   BatchEntry() : _charcode(UINT_MAX), _counter(0), _isCharCodeValid(false) { }
   BatchEntry(unsigned int charcode) : _charcode(charcode), _counter(0), _isCharCodeValid(true) { }
   BatchEntry(const BatchEntry& src, bool outline, bool shallowCopy = false);
   ~BatchEntry() { if (NULL != _glyph._lower._data) delete[] _glyph._lower._data; }

   bool _isCharCodeValid;
   unsigned int _charcode;
   unsigned int _counter;
   osgText::glyphEntry _glyph;

   static inline bool timeCompare(const BatchEntry* first, const BatchEntry* second)
   {
      return (first->_counter < second->_counter);
   }

   static void clearGlyphVector(std::vector<BatchEntry*>& glyphs);
};

class OSGBATCHEDTEXT_EXPORT GlyphInfoContainerBase
{
public:

   GlyphInfoContainerBase(const FontDescr& fontDescr, bool outline = true, unsigned int maxGlyphs = 500u);
   GlyphInfoContainerBase(const GlyphInfoContainerBase& src);

   /** Set the Font to use to render the text.*/
   void setFont(const osg::ref_ptr<osgText::Font>& font, unsigned int fontSize = 32u, unsigned int strokeWidth = 2u);
   void setKerningType(osgText::KerningType kerningType);
   bool getOutline() const;

   bool getActiveFontResolution(osgText::FontResolution& fontSize) const;
   bool getGlyph(bool isCharCode, const unsigned int charCodeOrGlyphIndex, osgText::glyphEntryUpper& upper, unsigned int& prevglyphindex, osg::Vec2& delta, bool isRightToLeft, bool readKerning = true);
   void constructDefaultGlyphs(const unsigned int* additionalCharCodes = NULL, unsigned int numCharCodes = 0);

   bool hasNewGlyphs() const;
   void getNewGlyphs(std::vector<BatchEntry*>& glyphs, bool shallowCopy = false);

   void addNewGlyphs(std::vector<BatchEntry*>& glyphs);

   typedef std::map<unsigned int, BatchEntry*> GlyphMap;

   void incCounter() const { ++_counter; }

   unsigned int getCounter() const { return _counter; }

   struct LineBreakCharCodesOrGlyphIndices
   {
      LineBreakCharCodesOrGlyphIndices(unsigned int space, unsigned int hyphen, unsigned int slash) : _space(space), _hyphen(hyphen), _slash(slash) {}

      unsigned int _space;//' '
      unsigned int _hyphen;//'-'
      unsigned int _slash;// '/'
   };

   const LineBreakCharCodesOrGlyphIndices& getLineBreakCharCodesOrGlyphIndices(const bool getCharCodes) const { return getCharCodes ? _lineBreakCharCodes : _lineBreakGlyphIndices; }

   void sortGlyphContainer()
   {
      _glyphContainer.sort(BatchEntry::timeCompare);
   }

   osgText::Font* getActiveFont() const;

protected:
   virtual ~GlyphInfoContainerBase();
   BatchEntry* addGlyphToCache(bool isCharCode, const unsigned int  charCodeOrGlyphIndex) const;

   /** Function checks if a previous query for the input glyph index has failed; If it has failed, it is not queried again*/
   bool isBlackListedGlyphIndex(unsigned int glyphIndex) const;

   /** deletext objects in the glyph map */
   void clearGlyphMap();

   virtual void addForDeletion(BatchEntry* batchEntry) const { delete batchEntry; }

   osg::ref_ptr<osgText::Font> _font;
   osgText::KerningType _kerningType;
   bool                 _hasKerning;
   bool                 _outline;
   unsigned int         _fontSize;
   unsigned int         _strokeWidth;
   unsigned int         _maxGlyphs;
   mutable unsigned int _counter;
   mutable GlyphMap     _glyphMapOfCharCodes;
   mutable GlyphMap     _glyphMapOfGlyphIndices;

   LineBreakCharCodesOrGlyphIndices _lineBreakGlyphIndices;
   LineBreakCharCodesOrGlyphIndices _lineBreakCharCodes;
   mutable std::list<BatchEntry*> _glyphContainer;
   mutable OpenThreads::Mutex  _glyphInfoContainerMutex;
   mutable std::vector<unsigned int> _blacklistedGlyphIndices;
   mutable std::vector<unsigned int> _newGlyphIndices;

private:
   GlyphInfoContainerBase& operator=(const GlyphInfoContainerBase&) /* = delete */;
};

class OSGBATCHEDTEXT_EXPORT GlyphInfoContainer : public GlyphInfoContainerBase, public osg::Referenced
{
public:
   GlyphInfoContainer(const FontDescr& fontDescr = FontDescr(), bool outline = true, unsigned int maxGlyphs = 500u);

protected:
   virtual ~GlyphInfoContainer() {};
};


inline GlyphInfoContainerBase::GlyphInfoContainerBase(const GlyphInfoContainerBase& src) :
   _font(src._font),
   _kerningType(src._kerningType),
   _hasKerning(src._hasKerning),
   _outline(src._outline),
   _fontSize(src._fontSize),
   _strokeWidth(src._strokeWidth),
   _maxGlyphs(src._maxGlyphs),
   _counter(0),
   _lineBreakCharCodes(src._lineBreakCharCodes),
   _lineBreakGlyphIndices(src._lineBreakGlyphIndices)
{

}

inline void GlyphInfoContainerBase::setKerningType(osgText::KerningType kerningType)
{
   _kerningType = kerningType;
}

inline osgText::Font* GlyphInfoContainerBase::getActiveFont() const
{
   return _font.get();
}

inline bool GlyphInfoContainerBase::getOutline() const
{
   return _outline;
}

inline bool GlyphInfoContainerBase::isBlackListedGlyphIndex(unsigned int glyphIndex) const
{
   return (_blacklistedGlyphIndices.end() == std::find(_blacklistedGlyphIndices.begin(), _blacklistedGlyphIndices.end(), glyphIndex) ? false : true);
}

}

#endif //GLYPH_INFO_CONTAINER
